"""
Copyright 2012 Wieser Dietmar, Margit Mutschlechner.

This file is part of Multimedia Social Networking Platform.

Multimedia Social Networking Platform is free software: 
you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Multimedia Social Networking Platform is distributed
in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Multimedia Social Networking Platform.  
If not, see <http://www.gnu.org/licenses/>.

"""

from msnp.service.utils import DateFormatter
from msnp import model
from msnp.service import network

class CustodyQueueType:
    """The different types of custody messages."""
    
    ALL_TYPES = 'allTypes'
    HELLO_RESULT_MESSAGE = 'helloResultMessage'
    AUTH_RESULT_MESSAGE = 'authResultMessage'
    RESP_IDENTITY_FOUND_MESSAGE = 'respIdentityFoundMessage'
    SHIFT_PEER_MESSAGE = 'shiftPeerMessage'
    GROUPS_RECEIVED = 'groupsReceived'
    GROUP_DETAILS_RECEIVED = 'groupDetailsReceived'
    MEMBER_STATUS_CHANGED = 'memberStatusChanged'
    MEDIA_CONTENT = 'mediaContent'
    GET_MEDIA_CONTENT_SINCE_LAST_SYNCH = 'mediaContentSinceLastSynch'
    PERSONAL_MESSAGE = 'personalMessage'
    PERSONAL_MESSAGE_CALLBACK = 'personalMessageCallback'
    
    @classmethod
    def getTypesToShift(cls):
        """This class method returns a list of custody message types, that have to be shifted during an upgrade of a peer.
        
        Returns:
            A list of custody message types.
        """
        
        typesToShift = []
        typesToShift.append(cls.AUTH_RESULT_MESSAGE)
        typesToShift.append(cls.GROUPS_RECEIVED)
        typesToShift.append(cls.GROUP_DETAILS_RECEIVED)
        typesToShift.append(cls.MEMBER_STATUS_CHANGED)
        typesToShift.append(cls.MEDIA_CONTENT)
        typesToShift.append(cls.GET_MEDIA_CONTENT_SINCE_LAST_SYNCH)
        typesToShift.append(cls.PERSONAL_MESSAGE)
        typesToShift.append(cls.PERSONAL_MESSAGE_CALLBACK)
        return typesToShift
    
    
#####################
##   SUPER_CLASS   ##
#####################

class CustodyMessage():
    """Super-class of all different custody messages that are stored at the responsible super peer of a low level peer.
    
    Attributes:
        __queueType: the appropriate type (string, defined in class CustodyQueueType)
        __name: the name of the low level peer (string)
    """
    
    def __init__(self, queueType, name):
        self.__queueType = queueType
        if name != None:
            name = unicode(name)
        self.__name = name
        
    def getQueueType(self):
        return self.__queueType
    
    def getName(self):
        return self.__name
        
    
#####################
##   SUB_CLASSES   ##
#####################

class AuthMessage(CustodyMessage):
    """Custody message for the authentication process.
    
    When a new peer authenticates at its responsible peer the response is stored as an authentication-message.
    It has an additional validation-type so that the new peer knows whether its authentication was successful or not.
    
    Attributes:
        __name: the name of the low level peer (string)
        __authValidationType: defined by PTPModel.ReturnType
    """
    
    def __init__(self, name, authValidationType):
        CustodyMessage.__init__(self, CustodyQueueType.AUTH_RESULT_MESSAGE, name)
        self.__authValidationType = authValidationType
    
    def getAuthValidationType(self):
        return self.__authValidationType
    
    
class HelloMessage(CustodyMessage):
    """Custody message for the hello process.
    
    When a new peer searches its responsible peer the result is stored at the entry-peer as a hello-message.
    It has in addition the responsible identity and a boolean to determine whether the proceeding authentication is a
    login or a registration.
    
    Attributes:
        __name: the name of the low level peer (string)
        __responsibleIdentity: the responsible identity for the new peer (PeerInfo instance)
        __isLogin: a boolean determining whether the proceeding authentication is a login or a registration
    """

    def __init__(self, name, responsibleIdentity, isLogin):
        CustodyMessage.__init__(self, CustodyQueueType.HELLO_RESULT_MESSAGE, name)
        self.__responsibleIdentity = responsibleIdentity
        self.__isLogin = isLogin
        
    def getResponsibleIdentity(self):
        return self.__responsibleIdentity
    
    def isLogin(self):
        return self.__isLogin
    
    
class ResponsibleIdentityFoundMessage(CustodyMessage):
    """Custody message for the finished search of a responsible identity.
    
    Attributes:
        __name: the name of the low level peer (string)
        __respIdentity: the searched responsible identity (PeerInfo instance)
        __searchedName: the name of the searched identity (string)
        __result: the result, defined by PTPModel.ReturnType
        __reactType: defined by Model.CallbackReactType
    """
    
    def __init__(self, name, respIdentity, searchedName, result, reactType):
        CustodyMessage.__init__(self, CustodyQueueType.RESP_IDENTITY_FOUND_MESSAGE, name)
        self.__respIdentity = respIdentity
        if searchedName != None:
            searchedName = unicode(searchedName)
        self.__searchedName = searchedName
        self.__result = result
        self.__reactType = reactType
        
    def getResponsibleIdentity(self):
        return self.__respIdentity
        
    def getSearchedName(self):
        return self.__searchedName
    
    def getResult(self):
        return self.__result
    
    def getReactType(self):
        return self.__reactType
    
    
class ShiftedPeerMessage(CustodyMessage):
    """Custody message for low level peers that have to be shifted during a peer upgrade.
    
    When a low level peer has to be shifted between two super peers a message is stored at the old responsible peer.
    
    Attributes:
        __name: the name of the low level peer (string)
        __newSuperPeer: the new responsible identity (PeerInfo instance)
    """

    def __init__(self, name, newSuperPeer):
        CustodyMessage.__init__(self, CustodyQueueType.SHIFT_PEER_MESSAGE, name)
        self.__newSuperPeer = newSuperPeer
        
    def getNewSuperPeer(self):
        return self.__newSuperPeer
    
    
class GroupsReicevedMessage(CustodyMessage):
    """Custody message for the finished group search.
    
    Attributes:
        __name: the name of the low level peer (string)
        __groups: a list of groups that correspond to the search criteria
        __reactType: defined by Model.CallbackReactType
        __searchFinished: a boolean which determines whether the search is finished or not.
    """
    
    def __init__(self, name, groups, reactType, searchFinished):
        CustodyMessage.__init__(self, CustodyQueueType.GROUPS_RECEIVED, name)
        self.__groups = groups
        self.__reactType = reactType
        self.__searchFinished = searchFinished
        
    def getGroups(self):
        return self.__groups
    
    def getReactType(self):
        return self.__reactType
    
    def getSearchFinished(self):
        return self.__searchFinished
        
        
class GroupDetailsReceivedMessage(CustodyMessage):
    """Custody message for the group details.
    
    Attributes:
        __name: the name of the low level peer (string)
        __group: a group (PTPModel.Group instance), containing all details.
        __reactType: defined by Model.CallbackReactType
    """
    
    def __init__(self, name, group, reactType):
        CustodyMessage.__init__(self, CustodyQueueType.GROUP_DETAILS_RECEIVED, name)
        self.__group = group
        self.__reactType = reactType
        
    def getGroup(self):
        return self.__group 
    
    def getReactType(self):
        return self.__reactType
    
    
class MemberStatusChangedMessage(CustodyMessage):
    """Custody message for membership status changes.
    
    Attributes:
        __name: the name of the low level peer (string)
        __membername: the name of the member (string)
        __groupanme: the name of the group (string)
        __ownername: the name of the owner (string)
        __result: the result, defined by PTPModel.ReturnType
        __status: the status
        __processed: determines whether the message is processed or not
        __timestamp: the time stamp
        __id: the id
    """
    
    def __init__(self, name, membername, groupname, ownername, result, status, processed, timestamp=None, id= -1):
        CustodyMessage.__init__(self, CustodyQueueType.MEMBER_STATUS_CHANGED, name)
        if membername != None:
            membername = unicode(membername)
        if groupname != None:
            groupname = unicode(groupname)
        if ownername != None:
            ownername = unicode(ownername)
        self.__membername = membername
        self.__groupanme = groupname
        self.__ownername = ownername
        self.__result = result
        self.__status = status
        self.__processed = processed
        self.__timestamp = timestamp
        if self.__timestamp == None:
            self.__timestamp = DateFormatter.formatTimeWithoutDay()
        self.__id = id
        
    def getMembername(self):
        return self.__membername
    
    def getGroupname(self):
        return self.__groupanme
    
    def getOwnername(self):
        return self.__ownername
    
    def getResult(self):
        return self.__result
    
    def getStatus(self):
        return self.__status
    
    def getTimestamp(self):
        return self.__timestamp
    
    def isProcessed(self):
        return self.__processed
    
    def setProcessed(self, processed=True):
        self.__processed = processed
    
    def getId(self):
        return self.__id
    
    
class MediaContentMessage(CustodyMessage):
    """Custody message for media contents.
    
    Attributes:
        __name: the name of the low level peer (string)
        __content: the transmitted media content.
        __timestamp: the time stamp
    """
    
    def __init__(self, name, content, timestamp):
        CustodyMessage.__init__(self, CustodyQueueType.MEDIA_CONTENT, name)
        self.__content = content
        self.__timestamp = timestamp
        
    def getContent(self):
        return self.__content
    
    def getTimestamp(self):
        return self.__timestamp


class GetMediaContentSinceLastSynchMessage(CustodyMessage):
    """Custody message for media contents since the last synchronization.
    
    Attributes:
        __name: the name of the low level peer (string)
        __senderIdentity: the sender (PeerInfo instance)
        __genres: a list of genres
        __senderName: the name of the sender
        __timestamp: the time stamp
    """
    
    def __init__(self, name, senderIdentity, senderName, genres, timestamp):
        CustodyMessage.__init__(self, CustodyQueueType.GET_MEDIA_CONTENT_SINCE_LAST_SYNCH, name)
        self.__senderIdentity = senderIdentity
        if senderName != None:
            senderName = unicode(senderName)
        self.__genres = genres
        self.__senderName = senderName
        self.__timestamp = timestamp
        
    def getSenderIdentity(self):
        return self.__senderIdentity
    
    def getSenderName(self):
        return self.__senderName
    
    def getGenres(self):
        return self.__genres
    
    def getTimestamp(self):
        return self.__timestamp
    
    
class PersonalMessage(CustodyMessage):
    """Custody message for personal message requests.
    
    Attributes:
        __name: the name of the low level peer (string)
        __senderName: the name of the sender
        __message: the message (string)
        __timestamp: the time stamp
    """
    
    def __init__(self, name, senderName, message, timestamp):
        CustodyMessage.__init__(self, CustodyQueueType.PERSONAL_MESSAGE, name)
        if senderName != None:
            senderName = unicode(senderName)
        if message != None:
            message = unicode(message)
        self.__senderName = senderName
        self.__message = message
        self.__timestamp = timestamp
        
    def getSendername(self):
        return self.__senderName
    
    def getMessage(self):
        return self.__message
    
    def getTimestamp(self):
        return self.__timestamp
    
    
class PersonalMessageCallback(CustodyMessage):
    """Custody message for personal message responds.
    
    Attributes:
        __name: the name of the low level peer (string)
        __senderName: the name of the sender
        __status: the status of the personal message request
        __timestamp: the time stamp
    """
    
    def __init__(self, name, senderName, timestamp, status):
        CustodyMessage.__init__(self, CustodyQueueType.PERSONAL_MESSAGE_CALLBACK, name)
        if senderName != None:
            senderName = unicode(senderName)
        self.__senderName = senderName
        self.__status = status
        self.__timestamp = timestamp
        
    def getSendername(self):
        return self.__senderName
    
    def getStatus(self):
        return self.__status
    
    def getTimestamp(self):
        return self.__timestamp
    

#####################
##    FUNCTIONS    ##
#####################

def constructMessage(keyValues, msgId= -1):
    """This function generates a new status changed message.
    
    Args:
        keyValues: a dictionary of key value pairs (string, string).
        msgId: the id of the message
        
    Returns:
        the generated MemberStatusChangedMessage instance, None if no queuetype is found
    """
    
    queueType = keyValues.get("queueType")
    name = keyValues.get("name")
    if queueType != None and name != None:
        if queueType == CustodyQueueType.MEMBER_STATUS_CHANGED:
            timestamp = keyValues.get("timestamp")
            membername = keyValues.get("membername")
            groupname = keyValues.get("groupname")
            ownername = keyValues.get("ownername")
            result = keyValues.get("result")
            status = keyValues.get("status")
            processed = True if keyValues.get("processed") == "1" else False
            if name != None and groupname != None and ownername != None and result != None and status != None:
                if len(status) == 0:
                    status = None
                return MemberStatusChangedMessage(name, membername, groupname, ownername, result, status, processed, timestamp, msgId)
    return None


def deconstructMessage(msg):
    """This function transforms a MemberStatusChangedMessage into a dictionary.
    
    Args:
        msg: the MemberStatusChangedMessage instance
        
    Returns:
        a dictinary (string, string), none if msg is none.
    """
    
    if msg != None:
        keyValues = {}
        queueType = msg.getQueueType()
        keyValues["queueType"] = queueType
        keyValues["name"] = msg.getName()
        if queueType == CustodyQueueType.MEMBER_STATUS_CHANGED: 
            keyValues["timestamp"] = str(msg.getTimestamp())
            keyValues["membername"] = msg.getMembername()
            keyValues["groupname"] = msg.getGroupname()
            keyValues["ownername"] = msg.getOwnername()
            keyValues["result"] = msg.getResult()
            if msg.getStatus() == None:
                keyValues["status"] = ""
            else:
                keyValues["status"] = msg.getStatus()
            keyValues["processed"] = "1" if msg.isProcessed() else "0"
            return keyValues
    return None
            
            
def isEqual(firstMsg, secondMsg):
    """This function determines whether two MemberStatusChangedMessage instances are identical.
    
    Args:
        firstMsg: the first MemberStatusChangedMessage instance
        secondMsg: the second MemberStatusChangedMessage instance
        
    Returns:
        True if the two messages are identical, False otherwise
    """
    
    if firstMsg != None and secondMsg != None:
        if firstMsg.getQueueType() == secondMsg.getQueueType() and firstMsg.getName() == secondMsg.getName():
            if firstMsg.getQueueType() == CustodyQueueType.MEMBER_STATUS_CHANGED:
                if firstMsg.getGroupname() == secondMsg.getGroupname():
                    if firstMsg.getMembername() == secondMsg.getMembername():
                        if firstMsg.getOwnername() == secondMsg.getOwnername():
                            if firstMsg.getResult() == secondMsg.getResult():
                                if firstMsg.getStatus() == secondMsg.getStatus():
                                    if firstMsg.getTimestamp() == secondMsg.getTimestamp():
                                        return True
    return False
    
    
def getReadableMessage(message):
    """This function transforms a MemberStatusChangedMessage instance into a readable string representation.
    
    Args:
        message: the MemberStatusChangedMessage instance
        
    Returns:
        a readable string representation of the given message
    """
    
    retString = ""
    if message.getQueueType() == CustodyQueueType.MEMBER_STATUS_CHANGED:
        myName = network.NetworkController.getMySelf().getMyIdentity().getName()
        membername = message.getMembername()
        groupname = message.getGroupname()
        ownername = message.getOwnername()
        result = message.getResult()
        status = message.getStatus()
        if status == None:
            if membername == myName:
                retString = "An error occurred while changing your membership status in group '" + groupname + "' with owner '" + ownername + "': " + model.PTPModel.MemberStatusChangedReturnType.toString(result) + "."
            else:
                retString = "An error occurred while changing the membership status of user '" + membername + "' in group '" + groupname + "' with owner '" + ownername + "': " + model.PTPModel.MemberStatusChangedReturnType.toString(result) + "."
        elif status == model.PTPModel.MemberStatus.ACTIVE:
            if membername == myName:
                retString = "You are now an active member of the group '" + groupname + "' with owner '" + ownername + "'."
            else:
                retString = "The user '" + membername + "' is now an active member of the group '" + groupname + "' with owner '" + ownername + "'."
        elif status == model.PTPModel.MemberStatus.NOT_CONFIRMED:
            if membername == myName:
                retString = "You have joined the group '" + groupname + "' with owner '" + ownername + "'. To be an active member the owner has to confirm the membership."
            else:
                retString = "The user '" + membername + "' has joined the group '" + groupname + "' with owner '" + ownername + "'. To become an active member you have to confirm the membership."
        elif status == model.PTPModel.MemberStatus.BLOCKED:
            if ownername == myName:
                retString = "You blocked user '" + membername + "' as owner of the group '" + groupname + "'."
            else:
                retString = "The user '" + ownername + "' that is owner of the group '" + groupname + "' blocked "
                if membername == myName:
                    retString += "you."
                else:
                    retString += "user '" + membername + "'."
        elif status == model.PTPModel.MemberStatus.DELETED:
            if membername == myName:
                retString = "You quitted the membership in group '" + groupname + "' with owner '" + ownername + "'."
            else:
                retString = "The user '" + membername + "' quitted the membership in group '" + groupname + "' with owner '" + ownername + "'."
    return retString
    
    
